//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008-2010, Shane J. M. Liesegang
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright 
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright 
//       notice, this list of conditions and the following disclaimer in the 
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the copyright holder nor the names of any 
//       contributors may be used to endorse or promote products derived from 
//       this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "Common.h"
#include "StringUtil.h"
#include "MathUtil.h"

#define theTuning Tuning::GetInstance()

///The class which handles getting and setting tuning variables. 
/** 
 * This class is used to handle housekeeping of variables you'll want to 
 *  manually change at runtime to affect the way your game plays. A good 
 *  example is the height a character can jump -- you might declare this as
 *  a tuning variable in the \c Config/tuning.ini, change it from the console
 *  using the \c tune() function, and then call SaveTuningVariables() from
 *  the console to save the new value back into the file. 
 * 
 * Some people find it helpful to bind certain tuning functions to keypresses
 *  while they're tuning, so you could increase or decrease a certain tuning
 *  variable with the arrow keys, for instance. 
 * 
 * It's also possible to declare new tuning variables at runtime and do
 *  all sorts of crazy things. Go nuts! 
 */
class Tuning
{
public:
        /**
         * Used to access the singleton instance of this class. As a shortcut, 
         *  you can just use "theTuning". 
         * 
         * @return The singleton
         */
        static Tuning& GetInstance();
        
        /** 
         * Get a list of the currently declared tuning variables. This only 
         *  gives you a set of variable names; you'll have to look up the 
         *  values if you want them. 
         * 
         * @return The set of strings representing the variable names. 
         */
        StringSet GetVariables();
        
        /**
         * Get an integer representation of a tuning variable. The engine will do
         *  its best to convert declared values into reasonable integer 
         *  representations, and will return 0 if it can't or if the variable has
         *  not been declared. 
         * 
         * @param name The name of the variable
         * @return Its integer representation
         */
        int GetInt(String name);
        
        /**
         * Get a floating point representation of a tuning variable. The engine
         *  will do its best to convert declared values into reasonable float 
         *  representations, and will return 0.0f if it can't or if the variable
         *  has not been declared. 
         * 
         * @param name The name of the variable
         * @return Its floating point representation
         */
        float GetFloat(String name);
        
        /**
         * Get a string representation of a tuning variable. If the variable was
         *  declared as another type, you'll still get a string representation of
         *  the value. Returns an empty string if the variable has not been 
         *  declared. 
         * 
         * @param name The name of the variable
         * @return Its string representation
         */
        String GetString(String name);
        
        /**
         * Get a Vector2 representation of a tuning variable. This guy is pretty
         *  much the odd man out in terms of conversions, since there's no obvious
         *  default way to convert between the different types and a 2d vector. 
         *  Returns a zero-length vector if you haven't actually set a vector to
         *  this variable or if the variable has not been declared. 
         * 
         * @param name The name of the variable
         * @return Its Vector2 representation
         */
        Vector2 GetVector(String name);
        
        /**
         * Change a tuning variable's value to a new integer. The Set* functions
         *  are also how you declare new tuning variables.
         * 
         * @param name The name of the variable to change
         * @param val The new integer value
         */
        void SetInt(String name, int val);
        
        /**
         * Change a tuning variable's value to a new float. The Set* functions
         *  are also how you declare new tuning variables.
         * 
         * @param name The name of the variable to change. 
         * @param val The new float value
         */
        void SetFloat(String name, float val);
        
        /**
         * Change a tuning variable's value to a new string. The Set* functions
         *  are also how you declare new tuning variables.
         * 
         * @param name The name of the variable to change. 
         * @param val The new string value
         */
        void SetString(String name, String val);
        
        /**
         * Change a tuning variable's value to a new vector. The Set* functions
         *  are also how you declare new tuning variables.
         * 
         * @param name The name of the variable to change. 
         * @param val The new vector value
         */
        void SetVector(String name, Vector2 val);
        
        /**
         * For internal usage -- lets the engine keep track of what variables have
         *  been tuned at runtime so they don't get stomped by the auto-reloading
         *  of the \c tuning.ini file. 
         * 
         * @param varName The name of the variable to add to the list. 
         */
        void AddToRuntimeTuningList(String varName);
        
        /**
         * For internal usage -- lets the engine keep check if a variable has
         *  been tuned at runtime so it doesn't get stomped when \c tuning.ini 
         *  gets reloaded. 
         * 
         * @param varName The name of the variable to check
         * @return Whether or not it's been tuned manually during this session
         */
        bool IsRuntimeTuned(String varName);
        
private:
        Tuning();
        static Tuning* s_Tuning;
        
        struct TuningVariable
        {
                int _int;
                float _float;
                String _string;
                Vector2 _vector;
                
                TuningVariable()
                {
                        _int = 0;
                        _float = 0.0f;
                        _string = "";
                        _vector = Vector2::Zero;
                }
                TuningVariable(int val)
                {
                        _int = val;
                        _float = (float)val;
                        _string = IntToString(val);
                        _vector = Vector2::Zero;
                }
                TuningVariable(float val)
                {
                        _int = MathUtil::RoundToInt(val);
                        _float = val;
                        _string = FloatToString(val);
                        _vector = Vector2::Zero;
                }
                TuningVariable(String val)
                {
                        _int = StringToInt(val); 
                        _float = StringToFloat(val);
                        _string = val;
                        _vector = Vector2::Zero;
                }
                TuningVariable(Vector2 val)
                {
                        _int = 0;
                        _float = 0.0f;
                        _string = "{" + FloatToString(val.X) + ", " + FloatToString(val.Y) + "}";
                        _vector = val;
                }
        };
        
        hashmap_ns::hash_map<String, TuningVariable> _tuningVariables;
        StringSet _runTimeTunedList;
};